#include "napi/native_api.h"
#include "scrypt.h"

static napi_value throw_error(napi_env env, const char* message)
{
    napi_throw_error(env, nullptr, message);
    return nullptr;
}

static napi_value parallelROMix(napi_env env, napi_callback_info info)
{
    size_t length;
    napi_status status;
    //
    size_t argc = 4;
    napi_value args[4] = {nullptr,nullptr,nullptr,nullptr};
    // arguments
    uint32_t* B;
    uint32_t N, r, p;

    // parse arguments
    status = napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
    if (status != napi_ok) {
        return throw_error(env, "Failed to parse arguments");
    }

    // check if args[0] is typed array
    napi_typedarray_type type;
    size_t byte_offset;
    napi_value array_buffer;
    status = napi_get_typedarray_info(env, args[0], &type, &length, (void**)&B, &array_buffer, &byte_offset);
    if (status != napi_ok || type != napi_uint32_array) {
        return throw_error(env, "Argument must be a Uint32Array");
    }

    // get N, r, p parameters
    status = napi_get_value_uint32(env, args[1], &N);
    if (status != napi_ok) {
        return throw_error(env, "Failed to parse N");
    }
    status = napi_get_value_uint32(env, args[2], &r);
    if (status != napi_ok) {
        return throw_error(env, "Failed to parse r");
    }
    status = napi_get_value_uint32(env, args[3], &p);
    if (status != napi_ok) {
        return throw_error(env, "Failed to parse p");
    }

    // execute parallel ROMix
    nativeParallelROMix(B, N, r, p);

    // return the modified Uint32Array
    return args[0];
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "parallelROMix", nullptr, parallelROMix, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "scrypt",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterScryptModule(void)
{
    napi_module_register(&demoModule);
}
